package com.liuqi.loggers.core.appender;

import com.liuqi.loggers.core.AbstractAppender;
import com.liuqi.loggers.core.Appender;
import com.liuqi.loggers.core.LoggerEvent;
import com.liuqi.loggers.core.LoggingException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 异步日志
 *
 * @author liuqi
 * @date 2024/6/6 14:26
 **/
public class AsyncAppender extends AbstractAppender {

    /**
     * 引用 appender
     */
    private final List<Appender> refAppends = new ArrayList<Appender>();

    /**
     * 日志缓存队列
     */
    private ArrayBlockingQueue<LoggerEvent> logBuffer;

    /**
     * 日志缓存队列最大深度
     */
    private int queueMaxSize;

    /**
     * 日志缓存队列满了是否丢弃日志
     */
    private boolean queueFullDiscarded;

    @Override
    protected void doAppend(LoggerEvent event) throws LoggingException {
        if (logBuffer.size() >= queueMaxSize && queueFullDiscarded) {
            return;
        }
        logBuffer.offer(event);
    }

    @Override
    public void start() {
        logBuffer = new ArrayBlockingQueue<LoggerEvent>(queueMaxSize);

        for (Appender appender : refAppends) {
            appender.start();
        }

        final ArrayList<LoggerEvent> outputLogs = new ArrayList<LoggerEvent>(logBuffer.size());
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (isStarted()) {
                    while (logBuffer.size() > 0) {
                        try {
                            logBuffer.drainTo(outputLogs);
                            for (LoggerEvent event : outputLogs) {
                                for (Appender appender : refAppends) {
                                    appender.append(event);
                                }
                            }
                            try {
                                for (Appender appender : refAppends) {
                                    if (appender instanceof OutputStreamAppender) {
                                        ((OutputStreamAppender) appender).flush();
                                    }
                                }
                            } catch (IOException e) {
                                throw new RuntimeException(e.getMessage(), e);
                            }
                        } finally {
                            outputLogs.clear();
                        }
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }, getName());
        t.setDaemon(true);
        t.start();

        super.start();
    }

    public void addRefAppends(Appender appender) {
        if (appender instanceof OutputStreamAppender) {
            ((OutputStreamAppender) appender).setImmediateFlush(false);
        }
        refAppends.add(appender);
    }

    public void addRefAppends(Appender... appends) {
        for (Appender appender : appends) {
            addRefAppends(appender);
        }
    }

    public List<Appender> getRefAppends() {
        return refAppends;
    }

    public ArrayBlockingQueue<LoggerEvent> getLogBuffer() {
        return logBuffer;
    }

    public int getQueueMaxSize() {
        return queueMaxSize;
    }

    public void setQueueMaxSize(int queueMaxSize) {
        this.queueMaxSize = queueMaxSize;
    }

    public boolean isQueueFullDiscarded() {
        return queueFullDiscarded;
    }

    public void setQueueFullDiscarded(boolean queueFullDiscarded) {
        this.queueFullDiscarded = queueFullDiscarded;
    }
}
